
#include <BFont>
#include <BApplication>
#include "member_BFont.h"

using namespace BWE;

#define member					(*(member_BFont*)_ptr)
#define member_allocate()		_ptr = new member_BFont(this)
#define member_release()		delete (member_BFont*)_ptr

BFont::BFont(int size)
{
	member_allocate();
	member.size = size;
}
BFont::BFont(const BString& family, int size)
{
	member_allocate();
	member.size = size;
	setFamily(family);
}
BFont::BFont(const BFont& other)
{
	member_allocate();
	this->copy(&other);
}
BFont::~BFont()
{
	member_release();
}

void BFont::copy(const BFont* other)
{
	this->setName(other->name());

	member.family = font_member(other)->family;
	member.encoding = font_member(other)->encoding;
	member.size = font_member(other)->size;
	member.bold = font_member(other)->bold;
	member.italic = font_member(other)->italic;
	member.monospace = font_member(other)->monospace;
	member.tabSize = font_member(other)->tabSize;

	member.codeInfoMap = font_member(other)->codeInfoMap;
	member.linearCodeInfoMap = font_member(other)->linearCodeInfoMap;
	member.dynamicCodeInfoMap = font_member(other)->dynamicCodeInfoMap;
	member.troubleCodes = font_member(other)->troubleCodes;
	member.fontFamily = font_member(other)->fontFamily;
}
BFont* BFont::clone() const
{
	BFont* font = new BFont();
	font->copy(this);
	return font;
}

bool BFont::empty() const
{
	return member.fontFamily.empty();
}
bool BFont::valid() const
{
	return member.fontFamily.valid();
}

bool BFont::setFamily(const BString& family)
{
	if (member.family != family)
	{
		member.family = family;
		member.codeInfoMap.clear();
		member.troubleCodes.clear();
		if (member.loadFamily())
		{
			emit(Signal_Dirty);
			return true;
		}
	}
	return false;
}
const BString& BFont::family() const
{
	return member.family;
}

bool BFont::setEncoding(Encoding encoding)
{
	if (member.encoding == encoding)
		return false;
	if (!member.fontFamily)
		return false;
	if (member.fontFamily->changeEncoding(encoding))
	{
		member.encoding = encoding;
		member.codeInfoMap.clear();
		member.troubleCodes.clear();
		emit(Signal_Dirty);
		return true;
	}
	return false;
}
Encoding BFont::encoding() const
{
	return member.encoding;
}

Encoding BFont::encoding(int index) const
{
	if (!member.fontFamily)
		return Encoding_None;
	return member.fontFamily->encodingList(index);
}
int BFont::encodingCount() const
{
	if (member.fontFamily)
		return member.fontFamily->encodingList.size();
	return 0;
}

bool BFont::setSize(int size)
{
	if (size < 5)
		return false;
	if (member.size != size)
	{
		member.size = size;
		member.codeInfoMap.clear();
		member.troubleCodes.clear();
		emit(Signal_Dirty);
		return true;
	}
	return false;
}
int BFont::size() const
{
	return member.size;
}

void BFont::setBold(bool bold)
{
	if (member.bold != bold)
	{
		member.bold = bold;
		emit(Signal_Dirty);
	}
}
bool BFont::bold() const
{
	return member.bold;
}

void BFont::setItalic(bool italic)
{
	if (member.italic != italic)
	{
		member.italic = italic;
		emit(Signal_Dirty);
	}
}
bool BFont::italic() const
{
	return member.italic;
}

void BFont::setMonospace(bool monospace)
{
	if (member.monospace != monospace)
	{
		member.monospace = monospace;
		BTexture::Filter filter = monospace ? BTexture::Filter_Linear : BTexture::Filter_Nearest;
		for (int i = 0; i < member.codeInfoMap.size(); i++)
		{
			CodeInfo& cinfo = member.codeInfoMap.seek(i).value();
			if (cinfo.image)
			{
				cinfo.image->setFilter(filter);
			}
		}
		emit(Signal_Dirty);
	}
}
bool BFont::monospace() const
{
	return member.monospace;
}

void BFont::setTabSize(int tabSize)
{
	if (member.tabSize != tabSize)
	{
		member.tabSize = tabSize;
		emit(Signal_Dirty);
	}
}
int BFont::tabSize() const
{
	return member.tabSize;
}

void BFont::setImage(const BCode& code, BImage* image)
{
	member.troubleCodes.remove(code);
	CodeInfo& info = member.codeInfoMap[code];
	info.image = image;
	info.top = image->height();
	emit(Signal_Dirty, code);
}
BImage* BFont::image(const BCode& code)
{
	CodeInfo* info = member.getCodeInfo(code);
	if (info)
		return info->image;
	return 0;
}
const BImage* BFont::image(const BCode& code) const
{
	CodeInfo* info = member.getCodeInfo(code);
	if (info)
		return info->image;
	return 0;
}

void BFont::setGlyph(const BCode& code, BGlyph* glyph)
{
	member.troubleCodes.remove(code);
	CodeInfo& info = member.codeInfoMap[code];
	info.glyph = glyph;
	emit(Signal_Dirty, code);
}
BGlyph* BFont::glyph(const BCode& code)
{
	CodeInfo* info = member.getCodeInfo(code);
	if (info)
		return info->glyph;
	return 0;
}
const BGlyph* BFont::glyph(const BCode& code) const
{
	CodeInfo* info = member.getCodeInfo(code);
	if (info)
		return info->glyph;
	return 0;
}

BSize BFont::codeSize(const BCode& code) const
{
	return member.codeSize(code);
}
int BFont::codeWidth(const BCode& code) const
{
	return member.codeSize(code).width();
}
int BFont::codeHeight(const BCode& code) const
{
	return member.codeSize(code).height();
}

BSize BFont::textSize(const BString& text, bool trimmed) const
{
	int x = 0;
	int totalWidth = 0;
	int totalHeight = trimmed ? 0 : member.size;
	for (auto it = text.begin(); it.valid(); ++it)
	{
		if (it == '\n')
		{
			x = 0;
			totalHeight += member.size;
		}
		if (it == '\t')
		{
			int times = x / member.size + 1;
			x = times * member.size;
			if (trimmed && totalHeight < member.size)
				totalHeight = member.size;
		}
		else
		{
			BSize codeSize = member.codeSize(*it);
			x += codeSize.width();
			if (trimmed && totalHeight < codeSize.height())
				totalHeight = codeSize.height();
		}
		if (totalWidth < x)
		{
			totalWidth = x;
		}
	}
	return BSize(totalWidth, totalHeight);
}
BSize BFont::textSize(const BString& text, int size) const
{
	int x = 0;
	int y = 0;
	int totalWidth = 0;
	int totalHeight = size;
	for (auto it = text.begin(); it.valid(); ++it)
	{
		if (it == '\n')
		{
			x = 0;
			totalHeight += size;
		}
		if (it == '\t')
		{
			int times = x / size + 1;
			x = times * size;
		}
		else
		{
			BSize codeSize = member.codeSize(*it, size);
			x += codeSize.width();
		}
		if (totalWidth < x)
		{
			totalWidth = x;
		}
	}
	if (totalWidth > 0)
		totalWidth += 1;
	if (totalHeight > 0)
		totalHeight += 1;
	return BSize(totalWidth, totalHeight);
}
